#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

#define BOARD_ROW 21
#define BOARD_COL 11
#define MY_CAR_STYLE '@'
#define MOB_CAR_STYLE '%'
#define BOARD_STYLE '*'
#define LIFE_STYLE '$'
#define STREET_STYLE ' '
#define SPOT_0 2
#define SPOT_1 5
#define SPOT_2 8
#define LIFE_BEGIN 5
#define TOP_SPEED 1
#define SPEED_PER_LEVER 1
#define START_SPEED 15
#define SCORE 10
#define CAR_PER_LEVEL 20

//--------------------- Interface ------------------------------------------------
void gotoxy(int x, int y)
{
    COORD pos = {x, y};
    HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(output, pos);
}

void MakeAPoint(int x, int y, char style)
{
    if ((x > 3 && x < BOARD_ROW-1 && y > 0 && y < BOARD_COL-1)||( style == BOARD_STYLE))
        //matrixInterface[x][y] = style;
    {
        gotoxy(y,x);
        printf("%c", style);
    }
}

void MakeCar(int x, int y, char style)
{
    switch (y)
    {
    case 0:
        y = SPOT_0;
        break;
    case 1:
        y = SPOT_1;
        break;
    case 2:
        y = SPOT_2;
        break;

    }
    MakeAPoint(x,y,style);
    MakeAPoint(x+1,y,style);
    MakeAPoint(x+2,y,style);
    MakeAPoint(x+3,y,style);

    MakeAPoint(x+1,y-1,style);
    MakeAPoint(x+1,y+1,style);
    MakeAPoint(x+3,y-1,style);
    MakeAPoint(x+3,y+1,style);
}

void GetStringLife( int life, char stringLife[])
{

    int i;

    if (life >LIFE_BEGIN)return;
    for (i = 0; i<life; i++)
    {
        stringLife[i] = LIFE_STYLE;
    }
    stringLife[life] = '\0';
}

void DrawBoard()
{
    int i,j;
    for (i = 3; i< BOARD_ROW; i++)
    {
        MakeAPoint(i, 0, BOARD_STYLE);
        MakeAPoint(i, BOARD_COL-1, BOARD_STYLE);
    }

    for (j = 1; j< BOARD_COL-1; j++)
    {
        MakeAPoint(3, j, BOARD_STYLE);
        MakeAPoint(BOARD_ROW-1, j, BOARD_STYLE);
    }

    /*for (i = 3; i< BOARD_ROW; i++)
    {
        for (j = 0; j< BOARD_COL; j++)
        {
            if ((i == 3)||(i == BOARD_ROW -1)||(j == 0)||(j == BOARD_COL -1))
                MakeAPoint(i,j,BOARD_STYLE);
            else
                MakeAPoint(i,j,STREET_STYLE);
        }
    }*/
}

void DrawInterface()
{
    printf(" Life:          Score:\n"/*, stringLife, score*/);
    printf(" Level :\n"/*, level*/);
    DrawBoard();
}

//---------------------/Interface ----------------------------------------------------


//---------------------- Core --------------------------------------------------------
void CreateMatrix(int matrix[BOARD_ROW][3])
{
    int i,j;
    for (i= 0; i< BOARD_ROW; i++)
    {
        for (j= 0; j< 3; j++)
        {
            matrix[i][j] = 0;
        }
    }
    matrix[BOARD_ROW-5][1] = 2;
}

void MobCarsMoving(int matrix[BOARD_ROW][3], int spot)
{
    int i,j;

    for (i = BOARD_ROW-1; i > 0; i--)
    {
        for (j = 0; j<3 ; j++)
        {
            if (matrix[i][j] == 1)
            {
                MakeCar(i , j , STREET_STYLE);
                MakeCar(i+1 , j , MOB_CAR_STYLE);
            }
            matrix[i][j] = matrix[i-1][j];
        }
    }
    matrix[0][0] = 0;
    matrix[0][1] = 0;
    matrix[0][2] = 0;

    matrix[BOARD_ROW -5][spot] = 2;
    matrix[BOARD_ROW -4][spot] = 0;


}

int RandomCarAppearType2()
{
    srand (time(NULL));
    return rand()%3;
}

int RandomCarAppearType1()
{
    srand (time(NULL));
    return rand()%2;
}

int RandomCarAppearType3()
{
    srand (time(NULL));
    return (rand()%2+1);
}


void ScoreCalculate(int matrix[BOARD_ROW][3],int *score)
{
    if (( matrix[BOARD_ROW - 5][0] == 1)||( matrix[BOARD_ROW - 5][1] == 1)||( matrix[BOARD_ROW - 5][2] == 1))
        *score += SCORE;
}

void KbHitted(int matrix[BOARD_ROW][3], int* spot, int* isRestart, int* isQuit, int *hit)
{
    char key;
    key = getch();

    switch (key)
    {
    case 75:
    {
        if (*spot == 1)
        {
            *spot = 0;
            if(matrix[BOARD_ROW - 5 ][0])
                *hit = 1;
            matrix[BOARD_ROW - 5 ][1] = 0;
            matrix[BOARD_ROW - 5 ][0] = 2;
            MakeCar(BOARD_ROW - 5, 1, STREET_STYLE);
            MakeCar(BOARD_ROW - 5, 0, MY_CAR_STYLE);
        }
        if (*spot == 2)
        {
            *spot = 1;
            if(matrix[BOARD_ROW - 5 ][1])
                *hit = 1;
            matrix[BOARD_ROW - 5 ][2] = 0;
            matrix[BOARD_ROW - 5 ][1] = 2;
            MakeCar(BOARD_ROW - 5, 2, STREET_STYLE);
            MakeCar(BOARD_ROW - 5, 1, MY_CAR_STYLE);
        }
        break;
    }
    case 77:
    {
        if (*spot == 1)
        {
            *spot = 2;
            if (matrix[BOARD_ROW - 5 ][2])
                *hit = 1;
            matrix[BOARD_ROW - 5 ][1] = 0;
            matrix[BOARD_ROW - 5 ][2] = 2;
            MakeCar(BOARD_ROW - 5, 1, STREET_STYLE);
            MakeCar(BOARD_ROW - 5, 2, MY_CAR_STYLE);
        }
        if (*spot == 0)
        {
            *spot = 1;
            if(matrix[BOARD_ROW - 5 ][1])
                *hit = 1;
            matrix[BOARD_ROW - 5 ][0] = 0;
            matrix[BOARD_ROW - 5 ][1] = 2;
            MakeCar(BOARD_ROW - 5, 0, STREET_STYLE);
            MakeCar(BOARD_ROW - 5, 1, MY_CAR_STYLE);
        }
        break;
    }
    case 'r':
    {
        *isRestart = 1;
        break;
    }
    case 'x':
    {
        *isQuit = 1;
        break;
    }
    }
}

void UpdateScore(int score)
{
    gotoxy(23, 0);
    printf("%d", score);
}

int IsHitted(int matrix[BOARD_ROW][3], int spot )
{
    int i;
    for (i = BOARD_ROW - 8; i< BOARD_ROW-1; i++)
        if (matrix[i][spot] == 1)
        {
            return 1;
        }
    return 0;
}

void UpdateLife ( int life)
{
    char strLife[10];
    GetStringLife(life, strLife);
    gotoxy(7, 0);
    printf("%s", strLife);
}

void UpdateLevel(int level)
{
    gotoxy(8,1);
    printf("%d  ", level);
}

int ConfirmText(char firstText[], char secondText[])
{
    char confirm;
    printf("%s", firstText);
    confirm = getch();
    while((confirm!='y')&&(confirm!='n')&&(confirm!='Y')&&(confirm!='N'))
    {
        printf("%s",secondText);
        confirm = getch();
    }
    if ((confirm=='y')||(confirm=='Y'))
        return 1;
    else return 0;
}

void Cracking ( int spot)
{
    _sleep(100);
    MakeCar(BOARD_ROW -5, spot, STREET_STYLE);
    _sleep(100);
    MakeCar(BOARD_ROW -5, spot, MY_CAR_STYLE);
    _sleep(100);
    MakeCar(BOARD_ROW -5, spot, STREET_STYLE);
    _sleep(100);
    MakeCar(BOARD_ROW -5, spot, MY_CAR_STYLE);
    _sleep(100);
}

void GameStart(int life, int score, int level)
{
    fflush(stdin);
    system("cls");
    int matrix[BOARD_ROW][3];
    CreateMatrix(matrix);
    int block;
    int gameTime = 0;
    int spot = 1;
    int space = 1;
    int mobSpot1, mobSpot2 = -1, mobSpot3 = -1;
    int randomType;
    int isRestart = 0, isQuit = 0;


    DrawInterface ();
    MakeCar(BOARD_ROW-5, 1, MY_CAR_STYLE);
    UpdateLife(life);
    UpdateLevel(level);
    while ( 1 )
    {
        int hit = 0;
        _sleep(10);
        int speed = START_SPEED - level +1;
        gameTime +=1;
        if (gameTime % speed == 0)
        {
            MobCarsMoving(matrix, spot);
            ScoreCalculate( matrix, &score);
            UpdateScore(score);
        }

        if ((gameTime % (speed * 5)) == 0)
        {

            srand (time(NULL));
            int probability;
            probability =  rand()%100;
            if ( probability <70 || space == 1)
            {
                if (mobSpot2 == 1 && mobSpot3 == 0)
                {
                    mobSpot1 = RandomCarAppearType1();
                    matrix[0][mobSpot1] = 1;
                }
                else if (mobSpot2 == 1 && mobSpot3 == 2)
                {
                    mobSpot1 = RandomCarAppearType3();
                    matrix[0][mobSpot1] = 1;
                }

                else
                {
                    mobSpot1 = RandomCarAppearType2();
                    matrix[0][mobSpot1] = 1;
                }


                if ( mobSpot2 != 1)
                {
                    mobSpot3 = mobSpot2;
                }
                mobSpot2 = mobSpot1;



                space = 0;
            }
            else space = 1;

        }

        if (kbhit())
        {
            KbHitted(matrix, &spot, &isRestart, &isQuit, &hit);
        }

        if ( IsHitted( matrix , spot ) || hit)
        {
            Cracking(spot);
            UpdateLife(life);
            if (life == 1)
            {
                system("cls");
                printf(" You havent any life left!\n ");
                if (ConfirmText(" Do you want to play again?\n \"y\" to play again \"n\" to exit. \n"," Your entered is invalid, do you want to play again?\n \"y\" to play again \"n\" to exit.\n"))
                    isRestart = 1;
                else isQuit = 1;
            }
            else
            {
                GameStart(life - 1 , score, level);
            }
        }

        if (isRestart)
        {
            Display();
        }

        if (isQuit)
        {
            system("cls");
            exit(0);
        }

        if (level > 13 )
        {
            level = 1;
        }
        if (gameTime == speed * 5 * CAR_PER_LEVEL)
        {
            level ++;
            gameTime = 0;
            UpdateLevel(level);
        }
    }
}

//---------------------- /Core -------------------------------------------------------

void Welcome()
{
    printf(" Welcome to car racing game !\n");
    printf(" Press x to exit, r to restart!\n");
    printf(" Loading: ");
    int i;
    for (i=0; i< 100; i++)
    {
        gotoxy(10,2);
        printf("%d%%", i+1);
        _sleep(100);
    }
}

void Display ()
{
    GameStart(LIFE_BEGIN, 0, 1);
}

void main()
{
    Welcome();
    system("cls");
    Display();

}




